package errors

import (
	"runtime"
	"strconv"
)

// FileFunc 文件函数信息
type FileFunc struct {
	Name string // 函数名
	File string // 包路径
}

// Func 函数信息
type Func struct {
	Line  int // 行号
	Entry uintptr
}

// Error 底层实现
func (f Func) Error() (str string) {
	str = "Line:" + strconv.FormatInt(int64(f.Line), 10) + "	Entry:"
	str += strconv.FormatInt(int64(f.Entry), 10)
	if file, ok := Stack.FilePc[f.Entry]; ok {
		str += "	Name:" + file.Name + "	File:"
		str += file.File
	}
	return str
}

// Stack 堆栈
var Stack struct {
	FuncPc map[uintptr]*Func     // 储存 Func 信息
	FilePc map[uintptr]*FileFunc // 储存 FileFunc 信息
	Caller func(skip int) *Func  // 得到第几个函数行
}

func init() {
	Stack.FuncPc = make(map[uintptr]*Func, 0)
	Stack.FilePc = make(map[uintptr]*FileFunc, 0)
	Stack.Caller = func(skip int) *Func {
		if skip < 0 {
			return nil
		}
		// 得到 Pc 信息
		if pc := make([]uintptr, 1); runtime.Callers(skip+2, pc) == 1 {
			// 直接返回
			if F, ok := Stack.FuncPc[pc[0]]; ok {
				return F
			}
			// 函数 PC 对象
			FuncPC := runtime.FuncForPC(pc[0])
			// 得到函数地址
			Entry := FuncPC.Entry()
			// 得到行数与文件路径
			file, line := FuncPC.FileLine(pc[0])
			// 暂存记录是否存在
			if _, ok := Stack.FilePc[Entry]; !ok {
				// 设置对象
				Stack.FilePc[Entry] = &FileFunc{
					Name: FuncPC.Name(),
					File: file,
				}
			}
			Stack.FuncPc[pc[0]] = &Func{
				Line:  line,
				Entry: Entry,
			}
			return Stack.FuncPc[pc[0]]
		}
		return nil
	}
}
